package com.terrace.core.mybatis.plugin.pagination;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import javax.sql.DataSource;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import com.terrace.core.mybatis.entity.Pagination;

/**
 * 
 * @author jiangyg
 *
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
@Intercepts(@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }))
public class PaginationHelper implements Interceptor {

	/**
	 * 分页处理程序
	 */
	private static final Map<String, PaginationHandler> PAGINATION_HANDLER_MAP = new ConcurrentHashMap<String, PaginationHandler>();
	
	/**
	 * 拦截器方法
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		
		// 未启用分页查询
		Pagination pagination = PaginationHandler.getLocalPagination();
		if (pagination == null) {
			return invocation.proceed();
		}
		
		// 拦截Executor
		if (invocation.getTarget() instanceof Executor) {
            return initPaginationHandler(invocation).process(invocation);
		}
		
		return null;
	}
	
	private PaginationHandler initPaginationHandler(Invocation invocation) {
		MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
		if (PAGINATION_HANDLER_MAP.containsKey(ms.getId())) {
			return PAGINATION_HANDLER_MAP.get(ms.getId());
		}
		ReentrantLock lock = new ReentrantLock();
		try {
			lock.lock();
			DataSource dataSource = ms.getConfiguration().getEnvironment().getDataSource();
			String url = getUrl(dataSource);
			Dialect dialect = Dialect.of(Dialect.fromJdbcUrl(url));
			PaginationHandler handler = new PaginationHandler(dialect);
			PAGINATION_HANDLER_MAP.put(ms.getId(), handler);
			return handler;
		} finally {
			lock.unlock();
		}
	}
	
	/**
	 * 只拦截Executor
	 */
	@Override
	public Object plugin(Object target) {
		if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
	}

	/**
	 * 设置属性值
	 */
	@Override
	public void setProperties(Properties properties) {
		
	}
	
	/**
	 * 准备分页
	 * @param pageNum
	 * @param pageSize
	 */
	public static void prepare(int pageNum, int pageSize) {
		PaginationHandler.setLocalPagination(new Pagination(pageNum, pageSize));
	}
	
	/**
	 * 准备分页
	 * @param isPagination	是否分页查询
	 */
	public static void prepare(boolean isPagination) {
		PaginationHandler.setLocalPagination(new Pagination<>(isPagination));
	}
	
	/**
	 * 准备分页
	 * @param isPagination	是否分页查询
	 * @param pageSize		页码
	 */
	public static void prepare(boolean isPagination, int pageSize) {
		PaginationHandler.setLocalPagination(new Pagination<>(pageSize, isPagination));
	}
	
	/**
	 * 准备分页
	 * @param pagination
	 */
	public static void prepare(Pagination pagination) {
		PaginationHandler.setLocalPagination(pagination);
	}
	
	/**
	 * 执行分页
	 * @return
	 */
	public static <T> Pagination<T> execute(List<T> list) {
		Pagination<T> page = PaginationHandler.getLocalPagination();
		PaginationHandler.removeLocalPagination();
		page.setResult(list);
		return page;
	}
	
	/**
	 * 根据数据源获取URL
	 * @param dataSource
	 * @return
	 */
	private String getUrl(DataSource dataSource) {
		Connection conn = null;
		try {
			conn = dataSource.getConnection();
			return conn.getMetaData().getURL();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
				}
			}
		}
	}
	
}
